package com.common.sdk.net.connect.http;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Animatable;
import android.net.Uri;

import com.android.sohu.sdk.common.toolbox.StringUtils;
import com.common.sdk.net.connect.fresco.CachedPostprocessor;
import com.common.sdk.net.connect.fresco.CustomRequestLoggingListener;
import com.common.sdk.net.connect.fresco.MyDefaultBitmapMemoryCacheParamsSupplier;
import com.common.sdk.net.connect.http.center.tools.CacheUtils;
import com.common.sdk.net.connect.http.util.BlockingLimitedStackQueue;
import com.common.sdk.net.connect.http.util.LimitedStackQueue;
import com.facebook.binaryresource.BinaryResource;
import com.facebook.binaryresource.FileBinaryResource;
import com.facebook.cache.common.CacheKey;
import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.common.internal.Supplier;
import com.facebook.common.logging.FLog;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.datasource.DataSubscriber;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeController;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.DraweeView;
import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory;
import com.facebook.imagepipeline.cache.MemoryCacheParams;
import com.facebook.imagepipeline.common.RotationOptions;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.core.ImagePipelineFactory;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableBitmap;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.listener.RequestListener;
import com.facebook.imagepipeline.listener.RequestLoggingListener;
import com.facebook.imagepipeline.producers.NetworkFetcher;
import com.facebook.imagepipeline.request.*;

import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by sangchengjiang on 16/8/4.
 * 用于图片请求并显示的单例类
 *
 * uri所支持的类型包括：
 * 1. http://  https://
 * 2. file://
 * 3. content://
 * 4. asset://
 * 5. res://
 * 6. data:mime/type;base64
 * 详情见http://frescolib.org/docs/supported-uris.html
 *
 * 如果有特殊的需求（使用Uri或者String作为参数的方法无法满足），则使用DraweeController作为参数的方法可实现更加灵活的、功能更加强大的图片请求
 */
public class ImageRequestManager {

    private static ImageRequestManager instance;

    private static final int QUEUE_SIZE = 50;
    private static final int CORE_SIZE = 2;
    private static final int MAX_SIZE = CORE_SIZE;

    private ImageRequestManager() {}

    public static void initialize(Context context) {
        Supplier<MemoryCacheParams> supplier = new MyDefaultBitmapMemoryCacheParamsSupplier(
                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)
        );

        initialize(context, supplier);
    }

    public static void initialize(Context context, Supplier<MemoryCacheParams> bitmapMemoryCacheParamsSupplier) {
        // 设置log监听
        Set<RequestListener> requestListenerSet = new HashSet<RequestListener>();
        requestListenerSet.add(new CustomRequestLoggingListener());

        // 设置队列
        BlockingQueue<Runnable> queue = new BlockingLimitedStackQueue<Runnable>(QUEUE_SIZE);
        ExecutorService executor = new ThreadPoolExecutor(CORE_SIZE, MAX_SIZE, 0, TimeUnit.SECONDS, queue);
        NetworkFetcher networkFetcher = new MyHttpUrlConnectionNetworkFetcher(executor);

        // 设置缓存
        DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(context)
                .setBaseDirectoryName(CacheUtils.getImageCacheName())
                .setBaseDirectoryPath(CacheUtils.getRootCacheDir())
                .setMaxCacheSize(CacheUtils.IMAGE_CACHE_SIZE)
                .build();

        ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
                .setNetworkFetcher(networkFetcher)
                .setRequestListeners(requestListenerSet)
                .setBitmapsConfig(Bitmap.Config.ARGB_8888)
                .setMainDiskCacheConfig(diskCacheConfig)
                .setBitmapMemoryCacheParamsSupplier(bitmapMemoryCacheParamsSupplier)
                .setDownsampleEnabled(true)
                .build();
        initialize(context, config);
    }

    public static void initialize(Context context, ImagePipelineConfig config) {
        Fresco.initialize(context, config);
    }

    public static void setDebug(boolean isDebug) {
        if (isDebug) {
            FLog.setMinimumLoggingLevel(FLog.DEBUG);
        } else {
            FLog.setMinimumLoggingLevel(FLog.ASSERT);
        }
    }

    public static ImageRequestManager getInstance() {
        if (instance == null) {
            synchronized (ImageRequestManager.class) {
                if (instance == null) {
                    instance = new ImageRequestManager();
                }
            }
        }
        return instance;
    }

    /**
     * 发起一个普通图片请求(只用于网络请求)
     * @param view 用于显示图片的view
     * @param url 图片地址的url
     */
    public void startImageRequest(DraweeView view, String url) {
        url = checkUrl(url);
        Uri uri = Uri.parse(url);
        startImageRequest(view, uri);
    }

    /**
     * 发起一个普通图片请求(只用于网络请求), 可对图片进行裁剪
     * @param view 用于显示图片的view
     * @param uri 图片地址的uri
     * @param width 要裁剪成的宽 (px)
     * @param height 要裁剪成的高 (px)
     */
    public void startImageRequest(DraweeView view, Uri uri, int width, int height) {
        CachedPostprocessor postprocessor = new CachedPostprocessor(width, height, uri);
        com.facebook.imagepipeline.request.ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setPostprocessor(postprocessor)
                .setRotationOptions(RotationOptions.autoRotateAtRenderTime())
                .build();

        PipelineDraweeController controller = (PipelineDraweeController)
                Fresco.newDraweeControllerBuilder()
                        .setImageRequest(request)
                        .setOldController(view.getController())
                        .build();
        view.setController(controller);
    }

    /**
     * 发起一个普通图片请求(只用于网络请求), 可对图片进行裁剪
     * @param view 用于显示图片的view
     * @param url 图片地址的url
     * @param width 要裁剪成的宽 (px)
     * @param height 要裁剪成的高 (px)
     */
    public void startImageRequest(DraweeView view, String url, int width, int height) {
        Uri uri = Uri.parse(url);
        startImageRequest(view, uri, width, height);
    }

    /**
     * 发起一个普通图片请求，controller可设置图片的uri、渐进属性、动画等，是一个功能强大的控制器，特殊需求的图片请求请使用此方法
     * 此类中的其他方法最终都是调用此方法完成请求
     * @param view 用于显示图片的view
     * @param controller 图片控制器
     */
    public void startImageRequest(DraweeView view, DraweeController controller) {
        if (controller == null) {
            return;
        }
        view.setController(controller);
    }

    /**
     * 发起一个普通图片请求
     * @param view 用于显示图片的view
     * @param uri 图片地址的uri
     */
    public void startImageRequest(DraweeView view, Uri uri) {
        if (uri == null) {
            return;
        }
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setUri(uri)
                .build();
        startImageRequest(view, controller);
    }

    /**
     * 请求并显示渐进图片，渐进图片只支持网络图片
     * @param view 用于显示图片的view
     * @param url 图片地址的url
     */
    public void startProgressiveRequest(DraweeView view, String url) {
        url = checkUrl(url);
        Uri uri = Uri.parse(url);
        com.facebook.imagepipeline.request.ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setProgressiveRenderingEnabled(true)
                .setRotationOptions(RotationOptions.autoRotateAtRenderTime())
                .build();
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(request)
                .setOldController(view.getController())
                .build();
        startImageRequest(view, controller);
    }

    /**
     * 请求并显示渐进图片，渐进图片只支持网络图片, 可对图片进行裁剪
     * @param view 用于显示图片的view
     * @param url 图片地址的url
     * @param width 要裁剪成的宽 (px)
     * @param height 要裁剪成的高 (px)
     */
    public void startProgressiveRequest(DraweeView view, String url, int width, int height) {
        url = checkUrl(url);
        Uri uri = Uri.parse(url);
        CachedPostprocessor postprocessor = new CachedPostprocessor(width, height, url);
        com.facebook.imagepipeline.request.ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setProgressiveRenderingEnabled(true)
                .setPostprocessor(postprocessor)
                .setRotationOptions(RotationOptions.autoRotateAtRenderTime())
                .build();
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(request)
                .setOldController(view.getController())
                .build();
        startImageRequest(view, controller);
    }

    /**
     * 发起一个gif图片的请求（只能从网络请求），也可用作普通图片请求，为支持普通图片与gif图片的view提供统一调用方法
     * @param view 用于显示图片的view
     * @param url 图片地址的url
     */
    public void startGifRequest(DraweeView view, String url) {
        url = checkUrl(url);
        Uri uri = Uri.parse(url);
        startGifRequest(view, uri);
    }

    /**
     * 发起一个gif图片的请求，也可用作普通图片请求，为支持普通图片与gif图片的view提供统一调用方法
     * @param view 用于显示图片的view
     * @param uri 图片地址的uri
     */
    public void startGifRequest(DraweeView view, Uri uri) {
        if (uri == null) {
            return;
        }
        DraweeController gifController = Fresco.newDraweeControllerBuilder()
                .setUri(uri)
                .setAutoPlayAnimations(true)
                .build();
        startImageRequest(view, gifController);
    }

    /**
     * 发起一个无缓存的图片请求
     * @param view 用于显示图片的view
     * @param url 图片地址的url
     */
    public void startImageRequestNoCache(DraweeView view, String url) {
        url = checkUrl(url);
        long time = System.currentTimeMillis();
        if (url.contains("?")) {
            url += "&t=" + time;
        } else {
            url += "?t=" + time;
        }
        startImageRequest(view, url);
    }

    /**
     * 发起一个只从内存缓存中取图片的请求
     * @param url 图片地址的url
     * @return bitmap(如果有)
     */
    public Bitmap startImageRequestCacheOnly(String url) {
        url = checkUrl(url);
        Uri uri = Uri.parse(url);
        com.facebook.imagepipeline.request.ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
                .setRotationOptions(RotationOptions.autoRotateAtRenderTime())
                .build();
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        DataSource<CloseableReference<CloseableImage>> dataSource =
                imagePipeline.fetchImageFromBitmapCache(imageRequest, null);
        CloseableReference<CloseableImage> imageReference = null;
        try {
            imageReference = dataSource.getResult();
            if (imageReference != null) {
                CloseableImage image = imageReference.get();
                if (image instanceof CloseableBitmap) {
                    return ((CloseableBitmap) image).getUnderlyingBitmap();
                }
            }
            return null;
        } finally {
            dataSource.close();
            CloseableReference.closeSafely(imageReference);
        }
    }

    /**
     * 判断bitmap是否在内存缓存中
     * @param url
     * @return
     */
    public boolean isInBitmapMemoryCache(String url) {
        url = checkUrl(url);
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        return imagePipeline.isInBitmapMemoryCache(Uri.parse(url));
    }

    public void evictMemoryCache(Uri uri) {
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        imagePipeline.evictFromMemoryCache(uri);
    }

    public void clearMemoryCache() {
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        imagePipeline.clearMemoryCaches();
    }

    /**
     * 预加载到内存，以便之后快速取
     * @param url
     */
    public void prefetchToBitmapCache(String url) {
        url = checkUrl(url);
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        Uri uri = Uri.parse(url);
        com.facebook.imagepipeline.request.ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
                .setRotationOptions(RotationOptions.autoRotateAtRenderTime())
                .build();
        imagePipeline.prefetchToBitmapCache(imageRequest, null);
    }

    /**
     * 发起一个异步请求，你可以在数据源的回调中实现自己的逻辑，
     * 比如当传递一个BaseBitmapDataSubscriber时，你可以在onNewResultImpl(bitmap)中获取到bitmap
     * 但是要注意此bitmap只能在此方法范围内使用，否则会被回收,如果需要在其他地方使用，需要复制一份bitmap
     * 除特殊情况，一般是用不到此方法的。对于大部分需要根据图片是否请求成功来作判断的需求，可以使用带controller参数的方法，controller里可以传递一个ControllerListener,里面可以得到图片请求成功与否的回调
     * 可以使用controller方法实现的，不要使用此方法
     * @param url
     * @param subscriber
     */
    public void startImageRequest(String url, DataSubscriber<CloseableReference<CloseableImage>> subscriber) {
        url = checkUrl(url);
        Uri uri = Uri.parse(url);
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        com.facebook.imagepipeline.request.ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
                .setRotationOptions(RotationOptions.autoRotateAtRenderTime())
                .build();
        DataSource<CloseableReference<CloseableImage>>
                dataSource = imagePipeline.fetchDecodedImage(imageRequest, null);
        dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
    }


    public File getImageCachedFile(String url) {
        com.facebook.imagepipeline.request.ImageRequest imageRequest = com.facebook.imagepipeline.request.ImageRequest.fromUri(Uri.parse(url));
        CacheKey cacheKey= DefaultCacheKeyFactory.getInstance()
                .getEncodedCacheKey(imageRequest, null);
        BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
        File file=((FileBinaryResource)resource).getFile();
        return file;
    }

    private String checkUrl(String url) {
        if (StringUtils.isBlank(url)) {
            url = "";
        }
        return url;
    }
}
